package com.market.plus.core.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import com.market.plus.core.dao.ParticipantDao;
import com.market.plus.core.domain.Album;
import com.market.plus.core.domain.AlbumType;
import com.market.plus.core.domain.ContentFile;
import com.market.plus.core.domain.Friend;
import com.market.plus.core.domain.LockStatus;
import com.market.plus.core.domain.Participant;
import com.market.plus.core.domain.Relationship;
import com.market.plus.core.domain.Status;
import com.market.plus.core.domain.UserInfo;
import com.market.plus.core.dto.AlbumDto;
import com.market.plus.core.dto.ParticipantDto;
import com.market.plus.core.dto.RelationshipDto;
import com.market.plus.core.dto.UserInfoDto;
import com.market.plus.core.mapping.DTOConfiguration;
import com.market.plus.core.mapping.DTOService;
import com.market.plus.core.mapping.DomainService;
import com.market.plus.core.model.CustomUserDetails;
import com.market.plus.core.model.CustomUserDetailsImpl;
import com.market.plus.core.model.MultipartFile;
import com.market.plus.core.service.MarketOCMService;
import com.market.plus.core.service.ParticipantService;
import com.market.plus.core.util.MarketUtil;
import com.opm.security.core.model.ExternalUserDetails;
import com.opm.security.core.util.UserUtil;

@Service("participantService")
@Transactional(readOnly = true)
public class ParticipantServiceImpl implements ParticipantService {

   private DomainService domainService;

   private DTOService dtoService;

   private ParticipantDao participantDao;

   private MarketOCMService marketOCMService;

   @Autowired
   public ParticipantServiceImpl(
       @Qualifier("participantDao")ParticipantDao participantDao,
       @Qualifier("marketDomainService") DomainService domainService,
       @Qualifier("marketOCMService") MarketOCMService marketOCMService,
       @Qualifier("marketDtoService") DTOService dtoService) {
      this.participantDao = participantDao;
      this.marketOCMService = marketOCMService;
      this.domainService = domainService;
      this.dtoService = dtoService;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
   public void create(ParticipantDto participantDto) {
      Participant participant = domainService.convertParticipant(participantDto);
      this.participantDao.save(participant);
   }

   public List<ParticipantDto> findAll(DTOConfiguration configuration) {
      List<Participant> participants = this.participantDao.findAll();
      List<ParticipantDto> participantDtos = this.dtoService.convertParticipantDtos(participants, configuration);
      return participantDtos;
   }

   public boolean hasUserNameExist(String username) {
      ExternalUserDetails userDetails = this.marketOCMService.loadUserByUsername(username);
      if (userDetails != null)
         return true;
      return false;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void register(ParticipantDto participantDto, CommonsMultipartFile avatarFile) throws Exception {
      Participant participant = this.domainService.convertParticipant(participantDto);
      Album album = new Album();
      album.setName("Avatar");
      album.setAlbumType(AlbumType.AVATAR);
      album.setParticipant(participant);
      List<Album> albums = new ArrayList<Album>();
      albums.add(album);
      participant.setAlbums(albums);
      UserInfoDto userInfoDto = participantDto.getUserInfo();
      String participantGUID = this.marketOCMService.addParticipant(participant, avatarFile, userInfoDto.getUsername(), userInfoDto.getPassword());
      participant.setGuid(participantGUID);
      this.participantDao.save(participant);
   }

   public List<AlbumDto> getAlbums(String participantGUID){
      List<AlbumDto> albumDtos = this.marketOCMService.getAlbums(participantGUID);
      return albumDtos;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
   public String addFileToAlbum(long participantId, AlbumDto album, MultipartFile multipartFile) throws Exception{
      String fileUUID = this.marketOCMService.addFiles(album.getAlbumGUID(), multipartFile);
      ContentFile contentFile = new ContentFile();
      contentFile.setTooltip(multipartFile.getTooltip());
      contentFile.setGuid(fileUUID);
      contentFile.setFileName(multipartFile.getFile().getOriginalFilename());
      contentFile.setLengthFile(multipartFile.getFile().getSize());
      contentFile.setMineType(multipartFile.getFile().getContentType());
      MarketUtil.setModifyedUser(contentFile, Status.NEW);

      Participant participant = this.participantDao.findById(participantId);
      participant.addContentFile(contentFile);

      this.participantDao.save(participant);
      return fileUUID;
   }

   public ParticipantDto findLoginUserName(boolean cache) {
      ParticipantDto participantDto = null;
      if (cache){
         participantDto = MarketUtil.getCurrentLogin();
      }
      else{
         Participant participant = this.participantDao.findParticipantByUsername(UserUtil.getCurrentUsername());
         participantDto = this.dtoService.convertParticipantDto(participant, null);
      }
      return participantDto;
   }

   public ParticipantDto findId(long participantId) {
      Participant participant = this.participantDao.findById(participantId);
      ParticipantDto participantDto = this.dtoService.convertParticipantDto(participant, null);
      return participantDto;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void addNewAlbum(long participantId, AlbumDto albumDto) throws Exception {
      Album album = this.domainService.convertAlbum(albumDto);
      MarketUtil.setModifyedUser(album, Status.NEW);
      Participant participant = this.participantDao.findById(participantId);
      participant.addAlbum(album);
      String albumGUID = this.marketOCMService.addAlbum(participant, album);
      album.setAlbumGUID(albumGUID);
      this.participantDao.save(participant);
   }

   public boolean hasAlbumNameExist(long participantId, String albumName) {
      Album album = this.participantDao.findByAlbumName(participantId, albumName);
      if (album != null)
         return true;
      return false;
   }

   public boolean hasRealtionshipExist(long participantId, String relationName) {
      Relationship relationship = this.participantDao.findByRelationship(participantId, relationName);
      if (relationship != null)
         return true;
      return false;
   }

   public ExternalUserDetails findByUsername(String username) {
      UserInfo userInfo = this.participantDao.findByUsername(username);
      if (userInfo == null){
         return null;
      }
      ExternalUserDetails userDetails = this.marketOCMService.loadUserByUsername(username);
      return userDetails;
   }

   public ExternalUserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
      ExternalUserDetails userDetails = this.findByUsername(username);
      CustomUserDetails customUserDetails = null;
      if (userDetails != null){
         customUserDetails = new CustomUserDetailsImpl();
         customUserDetails.setExtUserDetails(userDetails.getExtUserDetails());
         Participant participant = this.participantDao.findParticipantByUsername(username);
         ParticipantDto participantDto = this.dtoService.convertParticipantDto(participant, null);
         customUserDetails.setCurrentLoginUser(participantDto);
      }
      return customUserDetails;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void requestMakingFriend(long participantId, long friendParticipantId) {
      Participant participant = this.participantDao.findById(participantId);
      Participant friendInfo = this.participantDao.findById(friendParticipantId);
      Friend friend = new Friend();
      MarketUtil.setModifyedUser(friend);
      friend.setLockStatus(LockStatus.REQUESTED);
      friend.setHasPrivated(false);
      friend.setRequestedDate(friend.getCreatedDate());
      friend.setFriendInfo(friendInfo);
      friend.setParticipant(participant);
      participant.addFriend(friend);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void acceptFriend(long participantId, long friendId) {
      Participant participant = this.participantDao.findById(participantId);
      Participant friend = this.participantDao.findById(friendId);
      participant.acceptFriend(friend);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void denyFriends(long participantId, long[] friendIds) {
      Participant participant = this.participantDao.findById(participantId);
      participant.removeFriends(friendIds);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void udpateNewRelation(long participantId, RelationshipDto relationshipDto) throws Exception {
      Participant participant  = this.participantDao.findById(participantId);
      Relationship relationship = this.domainService.convertRelationship(relationshipDto);
      participant.addRelationship(relationship);
      this.participantDao.save(participant);
   }

   public List<ParticipantDto> waitingFriends(long participantId){
      List<Participant> participants = this.participantDao.findWaitingFriends(participantId);
      List<ParticipantDto> participantDtos = this.dtoService.convertParticipantDtos(participants, null);
      return participantDtos;
   }

   public List<ParticipantDto> listMyFriends(long participantId){
      List<Participant> participants = this.participantDao.findMyFriends(participantId);
      List<ParticipantDto> participantDtos = this.dtoService.convertParticipantDtos(participants, null);
      return participantDtos;
   }

   public List<ParticipantDto> listNotMyFriends(long participantId){
      List<Participant> participants = this.participantDao.findNotMyFriends(participantId);
      List<ParticipantDto> participantDtos = this.dtoService.convertParticipantDtos(participants, null);
      return participantDtos;
   }

   public List<RelationshipDto> listMyRelationship(long participantId){
      Participant participant = this.participantDao.findById(participantId);
      List<RelationshipDto> relationshipDtos = this.dtoService.convertRelationshipDtos(participant.getRelationships(), null);
      return relationshipDtos;
   }

   public List<ParticipantDto> listMyLockedFriends(long participantId){
      List<Participant> participants = this.participantDao.findMyLockedFriends(participantId);
      List<ParticipantDto> participantDtos = this.dtoService.convertParticipantDtos(participants, null);
      return participantDtos;
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void lockFriend(long participantId, long friendId) {
      Participant participant = this.participantDao.findById(participantId);
      participant.lockFriend(friendId);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void unlockFriend(long participantId, long friendId) {
      Participant participant = this.participantDao.findById(participantId);
      participant.unlockFriends(friendId);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public Long addNewRelationship(long participantId, RelationshipDto relationshipDto) {
      Relationship relationship = this.domainService.convertRelationship(relationshipDto);
      relationship.setHasPrivated(true);
      relationship.setLockStatus(LockStatus.AVAIABLE);
      Participant participant = this.participantDao.findById(participantId);
      participant.addRelationship(relationship);
      MarketUtil.setModifyedUser(relationship, Status.UPDATED);
      this.participantDao.save(participant);
      Relationship expected = participant.getRelationship(relationship.getRelationName());
      return expected.getId();
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void addFriendToRelationship(long participantId, long relationshipId, long friendId){
      Participant participant = this.participantDao.findById(participantId);
      participant.addFriendToRelationship(relationshipId, friendId);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void removeFriendFromRelationship(long participantId, long relationshipId, long friendId){
      Participant participant = this.participantDao.findById(participantId);
      participant.removeFriendFromRelationship(relationshipId, friendId);
      this.participantDao.save(participant);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
   public void removeRelationship(long participantId, long relationshipId) {
      Participant participant = this.participantDao.findById(participantId);
      participant.removeRelationship(relationshipId);
      this.participantDao.save(participant);
   }

   public List<ParticipantDto> filterMyFriends(long participantId, List<Long> excludeIds) {
      List<Participant> participants = this.participantDao.findMyFriends(participantId, excludeIds);
      List<ParticipantDto> participantDtos = this.dtoService.convertParticipantDtos(participants, null);
      return participantDtos;
   }
   
   public List<Long> listMyLockeds(long participantId){
      List<Participant> participants = this.participantDao.findMyLockedFriends(participantId);
      List<Long> myLockeds = new ArrayList<Long>();
      for (int index=0; index<participants.size(); index++){
         myLockeds.add(participants.get(index).getId());
      }
      return myLockeds;
   }
}
